export class PromiseEx extends Promise {
    resolve;
    reject;

    constructor(executor) {
        let resolve_;
        let reject_;
        super((resolve, reject) => {
            resolve_ = resolve;
            reject_ = reject;
            if (executor) {
                executor(resolve, reject);
            }
        });
        this.resolve = resolve_;
        this.reject = reject_;
    }
}

export class Lock {
    #lock = null;

    async acquire() {
        if (this.#lock) {
            return this.#lock;
        } else {
            this.#lock = new PromiseEx()
            return Promise.resolve();
        }
    }

    release() {
        if (this.#lock) {
            this.#lock.resolve();
            this.#lock = null;
        }
    }

    async with(fun) {
        await this.acquire();
        try {
            if (fun.constructor.name === 'AsyncFunction') {
                return await fun();
            } else {
                return fun();
            }
        } finally {
            this.release();
        }
    }
}

export function input() {
    return new Promise(resolve => {
        process.stdin.setEncoding('utf8');
        process.stdin.resume();
        process.stdin.on('data', data => {
            process.stdin.pause(); // stops after one line reads
            resolve(data.replace(/[\r\n]+$/, ''));
        });
    });
}